package com.gitee.wsl.collections.set

import com.gitee.wsl.collections.delegate.DelegatingMutableIterator
import com.gitee.wsl.collections.set.api.MultiSet
import kotlinx.io.IOException
import kotlin.jvm.Transient


/**
 * Abstract implementation of the [MultiSet] interface to simplify the
 * creation of subclass implementations.
 *
 *
 * Subclasses specify a Map implementation to use as the internal storage. The
 * map will be used to map multiset elements to a number; the number represents the
 * number of occurrences of that element in the multiset.
 *
 *
 * @param <E> the type held in the multiset.
 * @since 4.1
</E> */
abstract class AbstractMapMultiSet<E> : AbstractMultiSet<E> {
    /**
     * Inner class EntrySetIterator.
     *
     * @param <E> the element type.
    </E> */
    protected class EntrySetIterator<E>
    /**
     * Constructs a new instance.
     * @param decorated  the iterator to decorate
     * @param parent  the parent multiset
     */(
        /**
         * The source Iterator.
         */
        protected val decorated: MutableIterator<Map.Entry<E, MutableInteger>>,
        /** The parent map  */
        protected val parent: AbstractMapMultiSet<E>,
    ) : MutableIterator<MultiSet.Entry<E>> {
        /** The last returned entry  */
        protected var last: MultiSet.Entry<E>? = null

        /** Whether remove is allowed at present  */
        protected var canRemove: Boolean = false

        public override fun hasNext(): Boolean {
            return decorated.hasNext()
        }

        public override fun next(): MultiSet.Entry<E> {
            last = MultiSetEntry<E>(decorated.next())
            canRemove = true
            return last!!
        }

        public override fun remove() {
            check(canRemove) { "Iterator remove() can only be called once after next()" }
            decorated.remove()
            last = null
            canRemove = false
        }
    }

    /**
     * Inner class iterator for the MultiSet.
     */
    private class MapBasedMultiSetIterator<E>(private val parent: AbstractMapMultiSet<E>) : MutableIterator<E> {
        private val entryIterator = parent.map.entries.iterator()
        private var current: Map.Entry<E, MutableInteger>? = null
        private var itemCount = 0
        private val mods: Int = parent.modCount
        private var canRemove = false

        /** {@inheritDoc}  */
        public override fun hasNext(): Boolean {
            return itemCount > 0 || entryIterator.hasNext()
        }

        /** {@inheritDoc}  */
        public override fun next(): E {
            if (parent.modCount != mods) {
                throw ConcurrentModificationException()
            }
            if (itemCount == 0) {
                current = entryIterator.next()
                itemCount = current!!.value.value
            }
            canRemove = true
            itemCount--
            return current!!.key
        }

        /** {@inheritDoc}  */
        public override fun remove() {
            if (parent.modCount != mods) {
                throw ConcurrentModificationException()
            }
            check(canRemove)
            val mut: MutableInteger = current!!.value
            if (mut.value > 1) {
                mut.value--
            } else {
                entryIterator.remove()
            }
            parent.size--
            canRemove = false
        }
    }

    /**
     * Inner class MultiSetEntry.
     *
     * @param <E> the key type.
    </E> */
    protected class MultiSetEntry<E>(
        /**
         * The parent entry.
         */
        protected val parentEntry: Map.Entry<E, MutableInteger>,
    ) : AbstractEntry<E>() {
        override val count: Int
            get() = parentEntry.value.value

        override val element: E
            get() = parentEntry.key
    }

    /**
     * Mutable integer class for storing the data.
     */
    protected class MutableIntegerinternal constructor(
        /** The value of this mutable.  */
        var value: Int,
    ) {
        override fun equals(obj: Any?): Boolean {
            if (obj !is MutableInteger) {
                return false
            }
            return obj.value == value
        }

        override fun hashCode(): Int {
            return value
        }
    }

    /**
     * Inner class UniqueSetIterator.
     *
     * @param <E> the element type.
    </E> */
    protected class UniqueSetIterator<E> (
        iterator: MutableIterator<E>,
        /** The parent multiset  */
        protected val parent: AbstractMapMultiSet<E>,
    ) : DelegatingMutableIterator<E>(iterator) {
        /** The last returned element  */
        protected var lastElement: E? = null

        /** Whether remove is allowed at present  */
        protected var canRemove: Boolean = false

        public override fun next(): E {
            lastElement = super.next()
            canRemove = true
            return lastElement!!
        }

        public override fun remove() {
            check(canRemove) { "Iterator remove() can only be called once after next()" }
            val count = parent.getCount(lastElement!!)
            super.remove()
            parent.remove(lastElement!!, count)
            lastElement = null
            canRemove = false
        }
    }

    /** The map to use to store the data  */
    @Transient
    private lateinit var map: MutableMap<E, MutableInteger>

    /** The current total size of the multiset  */
    @Transient
    override var size = 0

    /** The modification count for fail fast iterators  */
    @Transient
    private var modCount = 0

    /**
     * Constructor needed for subclass serialisation.
     */
    //protected constructor()

    /**
     * Constructor that assigns the specified Map as the backing store. The map
     * must be empty and non-null.
     *
     * @param map the map to assign
     */
    protected constructor(map: MutableMap<E, MutableInteger>) {
        this.map = map
    }

    override fun add(`object`: E, occurrences: Int): Int {
        require(occurrences >= 0) { "Occurrences must not be negative." }

        val mut = map.get(`object`)
        val oldCount = mut?.value ?: 0

        if (occurrences > 0) {
            modCount++
            size += occurrences
            if (mut == null) {
                map.put(`object`, MutableInteger(occurrences))
            } else {
                mut.value += occurrences
            }
        }
        return oldCount
    }

    /**
     * Clears the multiset by clearing the underlying map.
     */
    override fun clear() {
        modCount++
        map.clear()
        size = 0
    }

    /**
     * Determines if the multiset contains the given element by checking if the
     * underlying map contains the element as a key.
     *
     * @param object the object to search for
     * @return true if the multiset contains the given element
     */
    override fun contains(`object`: E): Boolean {
        return map.containsKey(`object`)
    }

    override fun createEntrySetIterator(): MutableIterator<MultiSet.Entry<E>> {
        return EntrySetIterator<E>(map.entries.iterator(), this)
    }

     override fun createUniqueSetIterator(): MutableIterator<E> {
        return UniqueSetIterator<E>(map.keys.iterator(), this)
    }

    /**
     * Read the multiset in using a custom routine.
     * @param in the input stream
     * @throws IOException any of the usual I/O related exceptions
     * @throws ClassNotFoundException if the stream contains an object which class cannot be loaded
     * @throws ClassCastException if the stream does not contain the correct objects
     */
//    @Throws(IOException, ClassNotFoundException)
//    protected override fun doReadObject(`in`: ObjectInputStream) {
//        val entrySize: Int = `in`.readInt()
//        for (i in 0..<entrySize) {
//            val obj = `in`.readObject() as E?
//            val count: Int = `in`.readInt()
//            map.put(obj, MutableInteger(count))
//            size += count
//        }
//    }

    /**
     * Write the multiset out using a custom routine.
     * @param out the output stream
     * @throws IOException any of the usual I/O related exceptions
     */
//    @Throws(IOException::class)
//    protected override fun doWriteObject(out: ObjectOutputStream) {
//        out.writeInt(map.size())
//        for (entry in map.entrySet()) {
//            out.writeObject(entry.getKey())
//            out.writeInt(entry.getValue().value)
//        }
//    }

    override fun equals(`object`: Any?): Boolean {
        if (`object` === this) {
            return true
        }
        if (`object` !is MultiSet<*>) {
            return false
        }
        val other = `object` as MultiSet<E>
        if (other.size != size) {
            return false
        }
        for (element in map.keys) {
            if (other.getCount(element) != getCount(element)) {
                return false
            }
        }
        return true
    }

    /**
     * Returns the number of occurrence of the given element in this multiset by
     * looking up its count in the underlying map.
     *
     * @param element the object to search for
     * @return the number of occurrences of the object, zero if not found
     */
    override fun getCount(element: E): Int {
        val count = map[element]
        if (count != null) {
            return count.value
        }
        return 0
    }

    /**
     * Utility method for implementations to access the map that backs this multiset.
     * Not intended for interactive use outside of subclasses.
     *
     * @return the map being used by the MultiSet
     */
    protected fun getMap(): Map<E, MutableInteger> {
        return map
    }

    public override fun hashCode(): Int {
        var total = 0
        for (entry in map.entries) {
            val element = entry.key
            val count: MutableInteger = entry.value
            total += (element?.hashCode() ?: 0) xor count.value
        }
        return total
    }

    /**
     * Returns true if the underlying map is empty.
     *
     * @return true if multiset is empty
     */
    public override fun isEmpty(): Boolean {
        return map.isEmpty()
    }

    /**
     * Gets an iterator over the multiset elements. Elements present in the
     * MultiSet more than once will be returned repeatedly.
     *
     * @return the iterator
     */
    public override fun iterator(): MutableIterator<E> {
        return MapBasedMultiSetIterator<E>(this)
    }

    override fun remove(`object`: E, occurrences: Int): Int {
        require(occurrences >= 0) { "Occurrences must not be negative." }

        val mut = map.get(`object`)
        if (mut == null) {
            return 0
        }
        val oldCount = mut.value
        if (occurrences > 0) {
            modCount++
            if (occurrences < mut.value) {
                mut.value -= occurrences
                size -= occurrences
            } else {
                map.remove(`object`)
                size -= mut.value
                mut.value = 0
            }
        }
        return oldCount
    }

    /**
     * Sets the map being wrapped.
     *
     *
     * **Note:** this method should only be used during deserialization
     *
     *
     * @param map the map to wrap
     */


    /**
     * Returns an array of all of this multiset's elements.
     *
     * @return an array of all of this multiset's elements
     */
//    public override fun toArray(): Array<Any?> {
//        val result = kotlin.arrayOfNulls<Any>(size())
//        var i = 0
//        for (entry in map.entrySet()) {
//            val current: E? = entry.getKey()
//            val count: MutableInteger = entry.getValue()
//            for (index in count.value downTo 1) {
//                result[i++] = current
//            }
//        }
//        return result
//    }

    /**
     * Returns an array of all of this multiset's elements.
     * If the input array has more elements than are in the multiset,
     * trailing elements will be set to null.
     *
     * @param <T> the type of the array elements
     * @param array the array to populate
     * @return an array of all of this multiset's elements
     * @throws ArrayStoreException if the runtime type of the specified array is not
     * a supertype of the runtime type of the elements in this list
     * @throws NullPointerException if the specified array is null
    </T> */
//    public override fun <T> toArray(array: Array<T?>): Array<T?> {
//        var array = array
//        val size = size()
//        if (array.size < size) {
//            val unchecked = kotlin.reflect.jvm.internal.impl.load.kotlin.JvmType.Array.newInstance(
//                array.javaClass.getComponentType(),
//                size
//            ) as Array<T?>
//            array = unchecked
//        }
//
//        var i = 0
//        for (entry in map.entrySet()) {
//            val current: E? = entry.getKey()
//            val count: MutableInteger = entry.getValue()
//            for (index in count.value downTo 1) {
//                // unsafe, will throw ArrayStoreException if types are not compatible, see Javadoc
//                val unchecked = current as T?
//                array[i++] = unchecked
//            }
//        }
//        while (i < array.size) {
//            array[i++] = null
//        }
//        return array
//    }

    override fun uniqueElements(): Int {
        return map.size
    }
}


data class MutableInteger(var value: Int)

